
/**
\file
Definition of the viewport base class.
\author Sean Tromans
*/

#include <sun_pre_compiled.hpp>
#include <rainbow/viewport/viewport.hpp>
#include <rainbow/viewport/pass/pass.hpp>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#endif

namespace rainbow
{

	namespace detail
	{
#if !defined(SUN_DISABLE_LUA)

		/**
		Wrapper function around add_pass to allow std::strings to be sent in.
		\author Sean Tromans
		*/
		void add_pass( viewport_ptr viewport, std::string const & name, pass_ptr pass )
		{
			viewport->add_pass( name, pass );
		}

		/**
		Wrapper function around get_pass to allow std::strings to be sent in.
		\author Sean Tromans
		*/
		pass_ptr get_pass( viewport_ptr viewport, std::string const & name )
		{
			return viewport->get_pass( name );
		}

		/**
		Wrapper function around remove_pass to allow std::strings to be sent in.
		\author Sean Tromans
		*/
		pass_ptr remove_pass( viewport_ptr viewport, std::string const & name )
		{
			return viewport->remove_pass( name );
		}

		/**
		Wrapper class around viewport to allow lua to specialise it's own viewports.
		\author Sean Tromans
		*/
		class viewport_wrapper :	public viewport,
									public luabind::wrap_base
		{
			//
			//	Public Member Functions
			//
			public:

				/**
				Luabind seems to require this to have a constructor if it's a class. It works fine without one if it's a struct though/
				\author Sean Tromans
				*/
				viewport_wrapper( )
				{
				}

				/**
				Triggers this viewport to find all of the objects it would like to draw for each pass.
				@param step_fraction How far between logic updates this frame is.
				@param stack The viewport stack to add any additional required viewports to.
				\author Sean Tromans
				*/
				virtual void build_render_queue( float step_fraction, viewport_stack & stack )
				{
					call<void>( "build_render_queue", step_fraction, boost::ref( stack ) );
				}

				/**
				Informs the viewport that all dependencies have been met and it is now time to draw each of its passes.
				@param step_fraction How far between logic updates this frame is.
				\author Sean Tromans
				*/
				virtual void draw( float step_fraction )
				{
					call<void>( "draw", step_fraction );
				}

			//
			//	Static Member Functions
			//
			public:

				/**
				Default (null) build_render_queue function if lua does not provide a specialisation.
				\author Sean Tromans
				*/
				static void default_build_render_queue( viewport * /*vp*/, float /*step_fraction*/, viewport_stack & /*stack*/ )
				{
					throw sun::lua::not_implemented_exception( "rainbow.viewport.build_render_queue" );
				}

				/**
				Default (null) draw function if lua does not provide a specialisation.
				\author Sean Tromans
				*/
				static void default_draw( viewport * /*vp*/, float /*step_fraction*/ )
				{
					throw sun::lua::not_implemented_exception( "rainbow.viewport.draw" );
				}
		};

#endif
	}

	/**
	Adds a pass to the viewport.
	@param name The unique name to be given to this pass.
	@param pass The pass to add to the viewport.
	\throw name_already_used
	\author Sean Tromans
	*/
	void viewport::add_pass( nmn::hashed_string const & name, pass_ptr pass )
	{
		if ( get_pass( name ) != nullptr )
		{
			throw name_already_used( name.str( ) );
		}

		m_passes.push_back( std::make_pair( name, pass ) );
	}

	/**
	Retreives a pass from the viewport.
	@param name The unique name of the pass.
	@return The pass requested. Null if no pass with the unique name \c name existed.
	\author Sean Tromans
	*/
	pass_ptr viewport::get_pass( nmn::hashed_string const & name ) const
	{
		auto const iter = std::find_if( m_passes.cbegin( ), m_passes.cend( ), [&name]( pass_pair_t const & p )
		{
			return p.first == name;
		} );

		return iter != m_passes.cend( ) ? iter->second : pass_ptr( );
	}

	/**
	Removes a pass to from the viewport.
	@param name The unique name of the pass.
	@return The pass which was just removed. Null if no pass with the unique name \c name existed.
	\author Sean Tromans
	*/
	pass_ptr viewport::remove_pass( nmn::hashed_string const & name )
	{
		auto const iter = std::find_if( m_passes.begin( ), m_passes.end( ), [&name]( pass_pair_t const & p )
		{
			return p.first == name;
		} );

		if ( iter != m_passes.end( ) )
		{
			pass_ptr out = iter->second;
			m_passes.erase( iter );
			return out;
		}

		return pass_ptr( );
	}

	/**
	Exposes the viewport to lua.
	@param L The Lua State being initialised.
	\author Sean Tromans.
	*/
	void viewport::lua_interface( lua_State * L )
	{
#if !defined(SUN_DISABLE_LUA)
		luabind::module( L, "rainbow" )
		[
			luabind::class_< viewport, detail::viewport_wrapper, viewport_ptr >( "viewport" )
				.def( luabind::constructor<>( ) )
				.def( "add_pass", &viewport::add_pass )
				.def( "get_pass", &viewport::get_pass )
				.def( "remove_pass", &viewport::remove_pass )
				.def( "build_render_queue", &viewport::build_render_queue, &detail::viewport_wrapper::default_build_render_queue )
				.def( "draw", &viewport::draw, &detail::viewport_wrapper::default_draw )

				.def( "add_pass", &detail::add_pass )
				.def( "get_pass", &detail::get_pass )
				.def( "remove_pass", &detail::remove_pass )
		];
#else
		NMN_UNUSED( L );
#endif
	}

};
